﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;

using IMLibrary;
using IMLibrary.IO; 
using IMLibrary.NetClient;
using IMLibrary.BinaryPacket;
using IMLibrary.Security;
using IMLibrary.Net.Enum;
using IMLibrary.WS;
using IMLibrary.WS.Server;

using Ourmsg.Enum;

namespace Ourmsg.FileTransmit
{
    /// <summary>
    /// UDP文件传输
    /// </summary>
    public class ImageClientV1 : FileTransmitBaseV1
    {
        #region 构造
        /// <summary>
        /// 
        /// </summary>
        /// <param name="tFileInfo"></param>
        /// <param name="serverIP"></param>
        /// <param name="Port"></param>
        /// <param name="isSend"></param>
        /// <param name="tcp"></param>
        public ImageClientV1(TFileInfo tFileInfo, string serverIP, int Port, bool isSend, bool tcp)
            : base(tFileInfo, serverIP, Port, isSend)
        {
            mtu = 1024;//1次上传1K
            TCP = tcp;
            //TCP = false;
            if (TCP)
            {
                if (wsClient == null)
                {
                    wsClient = new WebSocket(string.Format("ws://{0}:{1}/Images", ServerIP, ServerPort));
                    wsClient.SetCredentials("Ourmsg", "123456@Ourmsg.net", true);
                    //wsClient.OnClose += WsClient_OnClose;
                    wsClient.OnOpen += WsClient_OnOpen;
                    wsClient.OnMessage += WsClient_OnMessage;
                    wsClient.ConnectAsync();
                }
            }
            else
            {
                if (udpClient == null)
                {
                    udpClient = new UDPClient();
                    udpClient.RecivedMsg += udpClient_RecivedMsg;
                    udpClient.Connected += udpClient_Connected;
                    udpClient.Connect(ServerIP, ServerPort);
                }
            }
        }
        #endregion

        /// <summary>
        /// 
        /// </summary>
        WebSocket wsClient = null;
        /// <summary>
        /// 
        /// </summary>
        UDPClient udpClient = null;
        /// <summary>
        /// 
        /// </summary>
        bool TCP = false;


        public override void Dispose()
        {
            base.Dispose();
            if (udpClient != null)
            {
                if (udpClient.IsConnect)
                    udpClient.Disconnect();
                udpClient.Shutdown();
            }
            if (wsClient != null)
                wsClient.Close();
        }

        private void WsClient_OnMessage(object sender, MessageEventArgs e)
        { 
            PacketReceived(e.RawData);
        }

        private void WsClient_OnOpen(object sender, EventArgs e)
        {
            Console.WriteLine("conneced!");
            OnSocketConnected();
        }

        private void WsClient_OnClose(object sender, CloseEventArgs e)
        {
            Console.WriteLine("conn close !");
        }
        
        void udpClient_Connected(object sender, IMLibrary.UDPEventArgs e)
        {
            OnSocketConnected();
        }

        void OnSocketConnected()
        {
            Console.WriteLine("Connected!");
            OnConnected();//触发文件传输连接成功事件
            if (IsSend)//如果是上传文件
            {
                BinaryFileMsg fileMsg = new BinaryFileMsg();
                fileMsg.Length = TFileInfo.Length;
                fileMsg.CommandType = (byte)FileServerCommandType.New;
                fileMsg.MD5 = TFileInfo.MD5;
                fileMsg.Extension = TFileInfo.Extension;
                SendMsg(fileMsg.Header);
            }
            else
            {
                RequestSendFilePak();//如果是下载文件，则开始下载
            }
        }

        void PacketReceived(byte[] data)
        {
            if (data.Length < BinaryFileMsg.HeaderLength) return;
            Console.WriteLine("e.Data.Length" + data.Length.ToString());
            BinaryFileMsg fileMsg = new BinaryFileMsg(data);
            Console.WriteLine(fileMsg.PayloadLength.ToString());

            if (fileMsg != null)//如果收到的消息对像不为空
            {
                if (fileMsg.CommandType == (byte)FileServerCommandType.New)//服务器允许上传文件
                { OnTransmitBefore(); }//触发文件传输前事件
                else if (fileMsg.CommandType == (byte)FileServerCommandType.Set)
                { SendFile(fileMsg); }
                else if (fileMsg.CommandType == (byte)FileServerCommandType.Get)//发送文件到服务器
                { ReceivedFileBlock(fileMsg); }
                else if (fileMsg.CommandType == (byte)FileServerCommandType.Over)//文件传输结束
                {
                    Console.WriteLine("over!");
                    OnTransmitted();//触发文件传输完成事件
                    Dispose();
                }
            }
        }
        void udpClient_RecivedMsg(object sender, UDPEventArgs e)
        {
            PacketReceived(e.Data);
        } 
        #region 方法

        #region  发送消息到文件代理服务器
        /// <summary>
        /// TCP发送消息到文件代理服务器
        /// </summary>
        /// <param name="e"></param>
        protected void SendMsg(byte[] data)
        {
            if (TCP)
            {
                if (wsClient != null)
                    wsClient.Send(data);
            }
            else
            {
                if (udpClient != null)
                    udpClient.Send(data);
            }
        }
        #endregion

        #region  发送文件
        /// <summary>
        /// TCP发送文件
        /// </summary>
        /// <param name="fileMsg"></param>
        protected void SendFile(BinaryFileMsg fileMsg)
        {
            long currLength = fileMsg.LastLength;

            if (currLength >= TFileInfo.Length)
            {
                OnTransmitted();//触发文件传输结束事件
                Dispose(); 
                return;//如果对方要求发送的数据块起始位置大于文件尺寸则认为是非法请求退出
            }

            if (!isTransmit)
            {
                isTransmit = true;//标记文件传输中
                OnTransmitBefore();//触发文件开始传输前事件
            }

            #region 如果当前是需要读写文件
            if (CurrRecLength % maxReadWriteFileBlock == 0)
            {
                CurrRecLength = 0;//断点清零,重新记忆

                if (fileBlock == null)
                    fileBlock = new byte[maxReadWriteFileBlock];

                //读文件到内存过程
                if ((TFileInfo.Length - currLength) < maxReadWriteFileBlock)//如果是最后一次读写文件，则将所有文件尾数据全部读入到内存
                    fileBlock = new byte[TFileInfo.Length - currLength];

                ////////////////////////文件操作
                FS = new FileStream(TFileInfo.fullName, FileMode.Open, FileAccess.Read, FileShare.Read);
                FS.Seek(currLength, SeekOrigin.Begin);//上次发送的位置
                FS.Read(fileBlock, 0, fileBlock.Length);
                FS.Close();
                FS.Dispose();
                /////////////////////////// 
            }
            #endregion

            long offSet = CurrRecLength % this.maxReadWriteFileBlock;// 获得要发送的绝对位置

            if (offSet + mtu > fileBlock.Length)
                buffer = new byte[fileBlock.Length - offSet];//要发送的缓冲区 

            Buffer.BlockCopy(fileBlock, (int)offSet, buffer, 0, buffer.Length);//将其保存于Buffer字节数组

            currLength += buffer.Length;
            CurrRecLength += buffer.Length;//断点设置

            fileMsg = new BinaryFileMsg();
            fileMsg.CommandType = (byte)FileServerCommandType.Set;//上传标记
            fileMsg.LastLength = currLength;
            fileMsg.Length = TFileInfo.Length;
            fileMsg.MD5 = TFileInfo.MD5;
            fileMsg.Extension = TFileInfo.Extension;
            fileMsg.Payload = buffer;

            Console.Write("文件：" + fileMsg.PayloadLength.ToString() + ";");
            SendMsg(fileMsg.ToArray());//发送文件到服务器

            TFileInfo.CurrLength = currLength;
            OnTransmitting();//触发收到或发送文件数据事件 

            if (currLength >= TFileInfo.Length)
            {
                OnTransmitted();//触发文件传输结束事件
                Dispose(); 
                return;//如果对方要求发送的数据块起始位置大于文件尺寸则认为是非法请求退出
            }
        }
        #endregion

        #region 处理收到的文件数据块
        /// <summary>
        /// 处理对方发送文件数据块
        /// </summary>
        private void ReceivedFileBlock(BinaryFileMsg fileMsg)//当对方发送文件数据块过来
        {
            if (fileMsg.LastLength > currGetPos)//如果发送过来的数据大于当前获得的数据
            {
                if (CurrRecLength % maxReadWriteFileBlock == 0)
                {
                    CurrRecLength = 0;//断点清零,重新记忆

                    if (fileBlock == null)
                        fileBlock = new byte[maxReadWriteFileBlock];

                    if ((TFileInfo.Length - currGetPos) < maxReadWriteFileBlock)//如果是最后一次读写文件，则将所有文件尾数据全部读入到内存
                        fileBlock = new byte[TFileInfo.Length - currGetPos];
                }

                long offSet = CurrRecLength % maxReadWriteFileBlock;// 获得要读写内存的绝对位置
                Buffer.BlockCopy(fileMsg.Payload, 0, fileBlock, (int)offSet, fileMsg.PayloadLength);//将其保存于Buffer字节数组

                currGetPos = fileMsg.LastLength;
                CurrRecLength += fileMsg.PayloadLength;//进行断点续传的继点记忆

                if (CurrRecLength % maxReadWriteFileBlock == 0 || currGetPos == TFileInfo.Length)
                {
                    ////////////////////////文件操作
                    FS = new FileStream(TFileInfo.fullName, FileMode.Append, FileAccess.Write, FileShare.Read);
                    FS.Write(fileBlock, 0, fileBlock.Length);
                    FS.Flush();
                    FS.Close();
                    FS.Dispose();
                    ///////////////////////////
                }

                TFileInfo.CurrLength = currGetPos;//设置当前已传输位置
                OnTransmitting();//触发收到或发送文件数据事件 

                if (currGetPos >= TFileInfo.Length)//如果文件传输完成，触发传输完成事件
                {
                    OnTransmitted();//触发文件传输完成事件;
                    Dispose(); 
                    return;//文件传输
                }
                RequestSendFilePak();//请求对方发送文件的下一数据包
            }
        }
        #endregion

        #region  请求发送文件数据包
        /// <summary>
        /// 请求发送文件数据包
        /// </summary>
        private void RequestSendFilePak()
        {
            BinaryFileMsg fileMsg = new BinaryFileMsg();
            fileMsg.CommandType = (byte)FileServerCommandType.Get;//标识下载
            fileMsg.Length = TFileInfo.Length;
            fileMsg.LastLength = currGetPos;
            fileMsg.MD5 = TFileInfo.MD5;//要下载的文件MD5值
            fileMsg.Extension = TFileInfo.Extension;
            SendMsg(fileMsg.Header);//请求服务器发送文件数据  
        }
        #endregion

        #endregion

    }
}
 